/************************************
 * Package: RGcpp					*
 * Authors: Rui Eduardo Gouveia Gil	*
 * Date: 2010						*
 ************************************/
#include "rgcpp/net/http/HTTPSession.h"
#include "rgcpp/tools/ToString.h"

namespace rgcpp
{

bool HTTPSession::globalInit=false;
boost::mutex HTTPSession::globalMut;

HTTPSession::HTTPSession() : debug(false), curl(NULL), caAutoAllow(false), timeout(0)
{
	HTTPSession::initGlobalCurl();

	curl = curl_easy_init();

	if(curl == NULL)
	{
		Throw(RuntimeException("Unable to init curl"));
	}

	curl_easy_setopt(curl,CURLOPT_COOKIEFILE,"");
	curl_easy_setopt(curl,CURLOPT_USERAGENT,"edgebox-eMI/1.0");
}

HTTPSession::~HTTPSession()
{
	if(curl)
		curl_easy_cleanup(curl);
}

void HTTPSession::setDebugMode(bool debug)
{
	this->debug=debug;

	curl_easy_setopt(curl,CURLOPT_VERBOSE,debug);
	curl_easy_setopt(curl,CURLOPT_HEADER,debug);
}

bool HTTPSession::isDebugMode()
{
	return this->debug;
}

void HTTPSession::setTimeout(unsigned int timeout)
{
	this->timeout = timeout;
	curl_easy_setopt(curl,CURLOPT_TIMEOUT,this->timeout);
}

unsigned int HTTPSession::getTimeout()
{
	return this->timeout;
}

void HTTPSession::setCookiesPath(const std::string& cookiesPath)
{
	this->cookiesPath=cookiesPath;

	curl_easy_setopt(curl,CURLOPT_COOKIEFILE,cookiesPath.c_str());
	curl_easy_setopt(curl,CURLOPT_COOKIEJAR,cookiesPath.c_str());
}

std::string HTTPSession::getCookiesPath()
{
	return this->cookiesPath;
}

void HTTPSession::setHttpAuth(const std::string& user, const std::string& pass)
{
	this->httpAuthData.t1=user;
	this->httpAuthData.t1=pass;

	curl_easy_setopt(curl,CURLOPT_USERPWD,(user+":"+pass).c_str());
}

Types<std::string,std::string> HTTPSession::getHttpAuth()
{
	return this->httpAuthData;
}

void HTTPSession::setCertificateAuthorityPath(const std::string& caPath)
{
	this->caPath=caPath;

	curl_easy_setopt(curl,CURLOPT_SSLCERTTYPE,"PEM");
	curl_easy_setopt(curl,CURLOPT_CAINFO,caPath.c_str());
}

std::string HTTPSession::getCertificateAuthorityPath()
{
	return this->caPath;
}

void HTTPSession::setCertificateAuthorityAutomaticAllow(bool caAutoAllow)
{
	this->caAutoAllow = true;

	curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,!caAutoAllow);
	curl_easy_setopt(curl,CURLOPT_SSL_VERIFYHOST,!caAutoAllow);
}

bool HTTPSession::isCertificateAuthorityAutomaticAllow()
{
	return this->caAutoAllow;
}

std::string HTTPSession::performGet(const std::string& uri)
{
	std::stringstream sstream;
	curl_easy_setopt(curl,CURLOPT_HTTPGET,1);
	curl_easy_setopt(curl,CURLOPT_WRITEDATA,&sstream);
	curl_easy_setopt(curl,CURLOPT_URL,uri.c_str());
	curl_easy_setopt(curl,CURLOPT_CUSTOMREQUEST,NULL);
	curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,&HTTPSession::responseStreamCallback);

	CURLcode result = curl_easy_perform(curl);

	if(result != CURLE_OK)
	{
		Throw(RuntimeException(std::string("Unable to perform request: ")+uri+std::string(" - curl error = ")+ToString(result)));
	}

	std::string resultString = sstream.str();
	return resultString;
}

void HTTPSession::performGetToNull(const std::string& uri)
{
	curl_easy_setopt(curl,CURLOPT_HTTPGET,1);
	curl_easy_setopt(curl,CURLOPT_WRITEDATA,NULL);
	curl_easy_setopt(curl,CURLOPT_URL,uri.c_str());
	curl_easy_setopt(curl,CURLOPT_CUSTOMREQUEST,NULL);
	curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,&HTTPSession::responseFileCallback);

	CURLcode result = curl_easy_perform(curl);

	if(result != CURLE_OK)
	{
		Throw(RuntimeException(std::string("Unable to perform request: ")+uri+std::string(" - curl error = ")+ToString(result)));
	}
}

void HTTPSession::performGetToFile(const std::string& uri, const std::string& filePath)
{
	std::ofstream file(filePath.c_str(),std::ios_base::binary | std::ios_base::out | std::ios_base::trunc);
	if(!file.good())
	{
		Throw(RuntimeException(std::string("Unable to perform request: ")+uri+std::string(" - invalid file path = ")+filePath));
	}

	curl_easy_setopt(curl,CURLOPT_HTTPGET,1);
	curl_easy_setopt(curl,CURLOPT_WRITEDATA,&file);
	curl_easy_setopt(curl,CURLOPT_URL,uri.c_str());
	curl_easy_setopt(curl,CURLOPT_CUSTOMREQUEST,NULL);
	curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,&HTTPSession::responseFileCallback);

	CURLcode result = curl_easy_perform(curl);
	file.close();

	if(result != CURLE_OK)
	{
		Throw(RuntimeException(std::string("Unable to perform request: ")+uri+std::string(" - curl error = ")+ToString(result)));
	}
}

void HTTPSession::performPut(const std::string& uri, const std::string& filePath)
{
	std::ifstream file(filePath.c_str(),std::ios_base::binary | std::ios_base::in);
	if(!file.good())
	{
		Throw(RuntimeException(std::string("Unable to perform request: ")+uri+std::string(" - invalid file path = ")+filePath));
	}

	file.seekg(0,std::ios::end);
	unsigned int size=file.tellg();
	file.seekg(0,std::ios::beg);

	curl_easy_setopt(curl,CURLOPT_PUT,1);
	curl_easy_setopt(curl,CURLOPT_URL,uri.c_str());
	curl_easy_setopt(curl,CURLOPT_CUSTOMREQUEST,NULL);
	curl_easy_setopt(curl,CURLOPT_INFILESIZE,size);
	curl_easy_setopt(curl,CURLOPT_READDATA,&file);
	curl_easy_setopt(curl,CURLOPT_READFUNCTION,&HTTPSession::requestFileCallback);

	CURLcode result = curl_easy_perform(curl);
	file.close();

	if(result != CURLE_OK)
	{
		Throw(RuntimeException(std::string("Unable to perform request: ")+uri+std::string(" - curl error = ")+ToString(result)));
	}
}

std::string HTTPSession::performPost(const std::string& uri, const std::string& data)
{
	std::stringstream istream;
	std::stringstream ostream;

	istream << data;
	curl_easy_setopt(curl,CURLOPT_COOKIEFILE,cookiesPath.c_str());
	curl_easy_setopt(curl,CURLOPT_COOKIEJAR,cookiesPath.c_str());

	curl_easy_setopt(curl,CURLOPT_POST,1);
	curl_easy_setopt(curl,CURLOPT_URL,uri.c_str());
	curl_easy_setopt(curl,CURLOPT_POSTFIELDS,NULL);
	curl_easy_setopt(curl,CURLOPT_CUSTOMREQUEST,NULL);
	curl_easy_setopt(curl,CURLOPT_POSTFIELDSIZE,data.size());
	curl_easy_setopt(curl,CURLOPT_READDATA,&istream);
	curl_easy_setopt(curl,CURLOPT_READFUNCTION,&HTTPSession::requestStreamCallback);
	curl_easy_setopt(curl,CURLOPT_WRITEDATA,&ostream);
	curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,&HTTPSession::responseStreamCallback);

	CURLcode result = curl_easy_perform(curl);

	if(result != CURLE_OK)
	{
		Throw(RuntimeException(std::string("Unable to perform request: ")+uri+std::string(" - curl error = ")+ToString(result)));
	}

	std::string resultString = ostream.str();
	return resultString;
}

std::string HTTPSession::performPostFromFile(const std::string& uri, const std::string& filePath)
{
	std::ifstream file(filePath.c_str(),std::ios_base::binary | std::ios_base::in);
	if(!file.good())
	{
		Throw(RuntimeException(std::string("Unable to perform request: ")+uri+std::string(" - invalid file path = ")+filePath));
	}

	file.seekg(0,std::ios::end);
	unsigned int size=file.tellg();
	file.seekg(0,std::ios::beg);

	std::stringstream sstream;
	curl_easy_setopt(curl,CURLOPT_POST,1);
	curl_easy_setopt(curl,CURLOPT_URL,uri.c_str());
	curl_easy_setopt(curl,CURLOPT_POSTFIELDS,NULL);
	curl_easy_setopt(curl,CURLOPT_CUSTOMREQUEST,NULL);
	curl_easy_setopt(curl,CURLOPT_POSTFIELDSIZE,size);
	curl_easy_setopt(curl,CURLOPT_READDATA,&file);
	curl_easy_setopt(curl,CURLOPT_READFUNCTION,&HTTPSession::requestFileCallback);
	curl_easy_setopt(curl,CURLOPT_WRITEDATA,&sstream);
	curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,&HTTPSession::responseStreamCallback);

	CURLcode result = curl_easy_perform(curl);
	file.close();

	if(result != CURLE_OK)
	{
		Throw(RuntimeException(std::string("Unable to perform request: ")+uri+std::string(" - curl error = ")+ToString(result)));
	}

	std::string resultString = sstream.str();
	return resultString;
}

void HTTPSession::performPostFromFileToFile(const std::string& uri, const std::string& srcFilePath, const std::string& dstFilePath)
{
	std::ifstream ifile(srcFilePath.c_str(),std::ios_base::binary | std::ios_base::in);
	if(!ifile.good())
	{
		Throw(RuntimeException(std::string("Unable to perform request: ")+uri+std::string(" - invalid input file path = ")+srcFilePath));
	}

	std::ofstream ofile(dstFilePath.c_str(),std::ios_base::binary | std::ios_base::out | std::ios_base::trunc);
	if(!ofile.good())
	{
		Throw(RuntimeException(std::string("Unable to perform request: ")+uri+std::string(" - invalid file path = ")+dstFilePath));
	}

	ifile.seekg(0,std::ios::end);
	unsigned int size=ifile.tellg();
	ifile.seekg(0,std::ios::beg);

	std::stringstream sstream;
	curl_easy_setopt(curl,CURLOPT_POST,1);
	curl_easy_setopt(curl,CURLOPT_URL,uri.c_str());
	curl_easy_setopt(curl,CURLOPT_POSTFIELDS,NULL);
	curl_easy_setopt(curl,CURLOPT_CUSTOMREQUEST,NULL);
	curl_easy_setopt(curl,CURLOPT_POSTFIELDSIZE,size);
	curl_easy_setopt(curl,CURLOPT_READDATA,&ifile);
	curl_easy_setopt(curl,CURLOPT_READFUNCTION,&HTTPSession::requestFileCallback);
	curl_easy_setopt(curl,CURLOPT_WRITEDATA,&ofile);
	curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,&HTTPSession::responseFileCallback);

	CURLcode result = curl_easy_perform(curl);
	ifile.close();
	ofile.close();

	if(result != CURLE_OK)
	{
		Throw(RuntimeException(std::string("Unable to perform request: ")+uri+std::string(" - curl error = ")+ToString(result)));
	}
}

void HTTPSession::initGlobalCurl()
{
	if(HTTPSession::globalInit)
		return;

	boost::mutex::scoped_lock lock(HTTPSession::globalMut);

	curl_global_init(CURL_GLOBAL_ALL);
}

size_t HTTPSession::requestFileCallback(void* ptr, size_t size, size_t nmemb, void* stream)
{
	std::ifstream* file = (std::ifstream*)stream;
	return file->readsome((char*)ptr,size*nmemb);
}

size_t HTTPSession::requestStreamCallback(void* ptr, size_t size, size_t nmemb, void* stream)
{
	std::stringstream* sstream = (std::stringstream*)stream;
	return sstream->readsome((char*)ptr,size*nmemb);
}

size_t HTTPSession::responseFileCallback(void* ptr, size_t size, size_t nmemb, void* stream)
{
	if(stream)
	{
		std::ofstream* file = (std::ofstream*)stream;
		file->write((char *)ptr,(size*nmemb));
	}
	return (size * nmemb);
}

size_t HTTPSession::responseStreamCallback(void* ptr, size_t size, size_t nmemb, void* stream)
{
	std::stringstream* sstream = (std::stringstream*)stream;
	sstream->write((char *)ptr,(size*nmemb));
	return (size * nmemb);
}

}

